home *** CD-ROM | disk | FTP | other *** search
/ NOVA - For the NeXT Workstation / NOVA - For the NeXT Workstation.iso / SourceCode / AdobeExamples / NX_HitDetect / Bezier.m < prev    next >
Encoding:
Text File  |  1992-12-19  |  12.4 KB  |  480 lines

  1.  
  2. /*
  3.  * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
  4.  *
  5.  * (b)  If this Sample Code is distributed as part of the Display PostScript
  6.  *    System Software Development Kit from Adobe Systems Incorporated,
  7.  *    then this copy is designated as Development Software and its use is
  8.  *    subject to the terms of the License Agreement attached to such Kit.
  9.  *
  10.  * (c)  If this Sample Code is distributed independently, then the following
  11.  *    terms apply:
  12.  *
  13.  * (d)  This file may be freely copied and redistributed as long as:
  14.  *    1) Parts (a), (d), (e) and (f) continue to be included in the file,
  15.  *    2) If the file has been modified in any way, a notice of such
  16.  *      modification is conspicuously indicated.
  17.  *
  18.  * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
  19.  *    Adobe Systems Incorporated.
  20.  * 
  21.  * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
  22.  *    CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
  23.  *    AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
  24.  *    ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
  25.  *    OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
  26.  *    WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
  27.  *    WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
  28.  *    DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
  29.  *    FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
  30.  *    OF THIRD PARTY RIGHTS.
  31.  */
  32.  
  33. /*
  34.  *    Bezier.m
  35.  *
  36.  *    The methods here handle the actions necessary to create and draw
  37.  *    a bezier curve. This is the only graphical object in the application.
  38.  *    Some methods found here would probably be moved to a generic
  39.  *    graphical object class in a more general application.  Some methods
  40.  *    have special steps included because of the interface decisions
  41.  *    chosen for a bezier curve. When the first and last control points
  42.  *    are moved, the second and third control points, respectively, are
  43.  *    also moved. This has implications when scrolling and restraining the
  44.  *    drawing to the imagable area.
  45.  *
  46.  *    Version:    2.0
  47.  *    Author:    Ken Fromm
  48.  *    History:
  49.  *            03-07-91        Added this comment.
  50.  */
  51.  
  52. #import "Bezier.h"
  53. #import "DetectApp.h"
  54. #import "DocView.h"
  55. #import "DrawingView.h"
  56. #import "DrawingViewWraps.h"
  57. #import <appkit/nextstd.h>
  58. #import <dpsclient/dpsclient.h>
  59. #import <dpsclient/wraps.h>
  60.  
  61. @implementation Bezier
  62.  
  63. - initFrame:(NXRect *)frm
  64. {
  65.     self = [super init];
  66.  
  67.     NX_MALLOC(path.pts, float, PTS_BEZIER*2 + 4);
  68.     NX_MALLOC(path.ops, char, 3);
  69.  
  70.     [self create:frm];
  71.  
  72.     return self;
  73. }
  74.  
  75. /* Randomly select the points and calculate the bounds. */
  76. - create:(const NXRect *)frm
  77. {
  78.     int    i;
  79.     
  80.     LLX(path.pts) = LLY(path.pts) = 9999;
  81.     URX(path.pts) = URY(path.pts) = -9999;
  82.  
  83.     for (i = 0; i < PTS_BEZIER * 2; i += 2)
  84.     {
  85.         path.pts[i + 4] = rand () % ((int)frm->size.width -8) + frm->origin.x + 4;
  86.         path.pts[i + 5] = rand () % ((int)frm->size.height -8) +  frm ->origin.y + 4;
  87.  
  88.         LLX(path.pts) = MIN(LLX(path.pts), path.pts[i + 4]);
  89.         LLY(path.pts) = MIN(LLY(path.pts), path.pts[i + 5]);
  90.         URX(path.pts) = MAX(URX(path.pts), path.pts[i + 4]);
  91.         URY(path.pts) = MAX(URY(path.pts), path.pts[i + 5]);
  92.     }
  93.     path.num_pts = PTS_BEZIER * 2 + 4;
  94.     
  95.     /*
  96.     *  The first entry is set to dps_setbbox for hit detection but then reset
  97.     *  to dps_ucache when drawing (except when the user is changing
  98.     *  the shape of the object frequently.)
  99.     */
  100.     path.ops[0] = dps_setbbox;
  101.     path.ops[1] = dps_moveto;
  102.     path.ops[2] = dps_curveto;
  103.     path.num_ops = 3;
  104.  
  105.     return self;
  106. }
  107.  
  108. - (UPath *) getPath
  109. {
  110.     return &path;
  111. }
  112.  
  113. - copyPts:srcId
  114. {
  115.     UPath    *srcPath;
  116.     
  117.     srcPath  = [srcId  getPath];
  118.  
  119.     NX_MALLOC(path.pts, float, PTS_BEZIER*2 + 4);
  120.     NX_MALLOC(path.ops, char, 3);
  121.  
  122.     bcopy(srcPath->pts, path.pts, srcPath->num_pts* (sizeof(float)/sizeof(char)));
  123.     bcopy(srcPath->ops, path.ops, srcPath->num_ops);
  124.  
  125.     return self;
  126. }
  127.  
  128. /*
  129. *    Returns the bounds.  The flag variable determines whether the
  130. *    knobs should be factored in. They may need to be for drawing but
  131. *    might not if needed for constraining reasons.
  132. */
  133. - getBounds:(NXRect *)bRect  withKnobs:(BOOL) flag
  134. {
  135.     float        knobsize;
  136.  
  137.     bRect->origin.x = LLX(path.pts);
  138.     bRect->origin.y = LLY(path.pts);
  139.     bRect->size.width = URX(path.pts) - LLX(path.pts);
  140.     bRect->size.height = URY(path.pts) -LLY(path.pts);
  141.  
  142.     if (flag)
  143.     {
  144.         knobsize = -[[[NXApp getDrawingView]  superview]  controlPointSize]/2;    
  145.         NXInsetRect(bRect, knobsize, knobsize);
  146.         NXIntegralRect(bRect);
  147.     }
  148.  
  149.     return self;
  150. }
  151.  
  152. /* Given the point number, return the point. */
  153. - getPoint:(int) pt_num  :(NXPoint *) pt
  154. {    
  155.     pt->x = path.pts[pt_num*2 + 4];
  156.     pt->y = path.pts[pt_num*2 + 5];
  157.     
  158.     return self;
  159. }
  160.  
  161. /*
  162. *    Depending on the pt_num passed in, return the rectangle
  163. *    that should be used for scrolling purposes. When the rectangle
  164. *    passes out of the visible rectangle then the screen should
  165. *    scroll. If the first and last points are selected, then the second
  166. *    and third points are included in the rectangle. If the second and
  167. *    third points are selected, then they are used by themselves.
  168. */
  169. - getScrollRect:(int) pt_num  :(NXRect *) aRect
  170. {
  171.     float            knobsize;
  172.  
  173.     if (pt_num == -1)
  174.     {
  175.         [self  getBounds:aRect  withKnobs:NO];     
  176.     }
  177.     else if (pt_num == 0)
  178.     {    
  179.         aRect->origin.x = MIN(path.pts[4], path.pts[6]);
  180.         aRect->origin.y = MIN(path.pts[5], path.pts[7]);
  181.         aRect->size.width = ABS(path.pts[4] - path.pts[6]);
  182.         aRect->size.height = ABS(path.pts[5] - path.pts[7]);
  183.     }
  184.     else if (pt_num == 3) 
  185.     {
  186.         aRect->origin.x = MIN(path.pts[10], path.pts[8]);
  187.         aRect->origin.y = MIN(path.pts[11], path.pts[9]);
  188.         aRect->size.width = ABS(path.pts[10] - path.pts[8]);
  189.         aRect->size.height = ABS(path.pts[11] - path.pts[9]);
  190.     }
  191.     else
  192.     {
  193.         aRect->origin.x = path.pts[pt_num*2 + 4];
  194.         aRect->origin.y = path.pts[pt_num*2 + 5];
  195.         aRect->size.width = 0;
  196.         aRect->size.height = 0;
  197.     }
  198.  
  199.     knobsize = -[[[NXApp  getDrawingView]  superview]  controlPointSize]/2;
  200.     NXInsetRect(aRect, knobsize, knobsize);    
  201.  
  202.     return self;
  203. }
  204.  
  205. /* 
  206. *    This method constains the point to the bounds of the view passed
  207. *    in. Like the method above, the constaining is dependent on the
  208. *    control point that has been selected.
  209. */
  210. - constrainPoint:(NXPoint *)aPt  andNumber:(int) pt_num  toView:aView
  211. {
  212.     float            knobsize;
  213.  
  214.     NXPoint        viewMax;
  215.     
  216.     NXPoint        *thisPt, *nextPt;
  217.  
  218.     NXRect        viewRect;
  219.  
  220.     [aView getBounds:&viewRect];
  221.     viewMax.x = viewRect.origin.x + viewRect.size.width;
  222.     viewMax.y = viewRect.origin.y + viewRect.size.height;
  223.  
  224.     if (pt_num == 0 || pt_num == 3)
  225.     {
  226.         thisPt = (NXPoint *) &path.pts[pt_num*2 + 4];
  227.         if (pt_num == 0)
  228.             nextPt = (NXPoint *) &path.pts[6];
  229.         else
  230.             nextPt = (NXPoint *) &path.pts[8];
  231.  
  232.         if (thisPt->x  >  nextPt->x)
  233.             viewRect.origin.x += thisPt->x  -  nextPt->x;
  234.         else
  235.             viewMax.x -= nextPt->x  -  thisPt->x;
  236.  
  237.         if (thisPt->y  >  nextPt->y)
  238.             viewRect.origin.y += thisPt->y - nextPt->y;
  239.         else
  240.             viewMax.y -= nextPt->y - thisPt->y;
  241.     }
  242.  
  243.     viewMax.x -= MARGIN;
  244.     viewMax.y -= MARGIN;
  245.     viewRect.origin.x += MARGIN;
  246.     viewRect.origin.y += MARGIN;
  247.  
  248.     aPt->x = MAX(viewRect.origin.x, aPt->x);
  249.     aPt->y = MAX(viewRect.origin.y, aPt->y);
  250.  
  251.     aPt->x = MIN(viewMax.x, aPt->x);    
  252.     aPt->y = MIN(viewMax.y, aPt->y);
  253.  
  254.     return self;
  255. }
  256.  
  257. /*
  258.  *    Change the point number passed in by the amount passed in in pt.
  259.  *    Recalculate the bounds because one of the bounding points could
  260.  *    have been the changed point.
  261.  */
  262. - changePoint:(int) pt_num  :(const NXPoint *) pt
  263. {
  264.     int        i;
  265.     
  266.     path.pts[pt_num *2 + 4] += pt->x;
  267.     path.pts[pt_num *2 + 5] += pt->y;
  268.  
  269.     LLX(path.pts) = LLY(path.pts) = 9999;
  270.     URX(path.pts) = URY(path.pts) = -9999;
  271.     for (i = 0; i < PTS_BEZIER * 2; i += 2)
  272.     {
  273.         LLX(path.pts) = MIN(LLX(path.pts), path.pts[i + 4]);
  274.         LLY(path.pts) = MIN(LLY(path.pts), path.pts[i + 5]);
  275.         URX(path.pts) = MAX(URX(path.pts), path.pts[i + 4]);
  276.         URY(path.pts) = MAX(URY(path.pts), path.pts[i + 5]);
  277.     }
  278.  
  279.     return self;    
  280. }
  281.  
  282. /*
  283. *    pt_num is the changing control point. pt holds the relative change in each coordinate. 
  284. *    The relative is needed and not the absolute because the closest inside control point
  285. *    changes when one of the outside points change.
  286. */
  287. - setPoint:(int) pt_num  :(const NXPoint *) pt
  288. {    
  289.     [self changePoint:pt_num :pt];
  290.     
  291.     if (pt_num == 0)
  292.         [self changePoint:1 :pt];
  293.     else if (pt_num == 3)
  294.         [self changePoint:2 :pt];
  295.     
  296.     return self;
  297. }
  298.  
  299. /* The pt argument holds the relative point change. */
  300. - moveAll:(const NXPoint *) pt
  301. {
  302.     int    i;
  303.  
  304.     for (i = 0; i < PTS_BEZIER * 2;  i += 2)
  305.     {
  306.         path.pts[i + 4] +=pt->x;
  307.         path.pts[i + 5] += pt->y;    
  308.     }
  309.  
  310.     LLX(path.pts) += pt->x;
  311.     LLY(path.pts) += pt->y;
  312.     URX(path.pts) += pt->x;
  313.     URY(path.pts) += pt->y;
  314.     
  315.     return self;
  316. }
  317.  
  318. /*
  319. *    Check for a control point hit. No need to perform the hit detection in
  320. *    the server since its a simple rectangle intersection check. Return the
  321. *    point number hit in the pt_num argument.
  322. */
  323. - (BOOL) hitControl:(const NXRect *)hitRect :(int *) pt_num  :(float) controlsize
  324. {
  325.     int        i;
  326.  
  327.     NXRect    knobRect;
  328.     
  329.     knobRect.size.width = knobRect.size.height = controlsize;
  330.     for (i=0; i < PTS_BEZIER*2; i += 2)
  331.     {
  332.         knobRect.origin.x = path.pts[i + 4] - controlsize/2;
  333.         knobRect.origin.y = path.pts[i + 5] - controlsize/2;
  334.         if (NXIntersectsRect(hitRect, &knobRect))
  335.         {
  336.             *pt_num = i/2;
  337.             return YES;
  338.         }
  339.     }
  340.  
  341.     return NO;
  342. }
  343.  
  344. /*
  345.  *  Check for hit dectection on the object. This uses the
  346.  *  inustroke operator to check for an intersection of the
  347.  *  hit detection rectangle with the path of the Bezier.
  348.  */
  349. - (BOOL) hitObject:(UPath *) hitUpath
  350. {
  351.     int        hit = NO;
  352.  
  353.     NXRect    aRect, bRect;
  354.     
  355.     NXSetRect(&aRect, hitUpath->pts[0], hitUpath->pts[1],
  356.             hitUpath->pts[2] - hitUpath->pts[0],
  357.              hitUpath->pts[3] - hitUpath->pts[1]);
  358.     [self getBounds:&bRect  withKnobs:NO];
  359.     if (NXIntersectsRect(&aRect, &bRect))
  360.     {
  361.         path.ops[0] = dps_setbbox;        
  362.         PSWHitPath(hitUpath->pts, hitUpath->num_pts, hitUpath->ops, hitUpath->num_ops,
  363.             path.pts, path.num_pts, path.ops, path.num_ops, &hit);
  364.     }
  365.     
  366.     return (BOOL) hit;
  367. }
  368.  
  369. /*
  370. *    Place the point locations and the chararacters for the control
  371. *    points into the user path description passed in. In this case, the
  372. *    xyshow operator is used instead of a user path. But because the
  373. *    xyshow operator takes the same data format as the user path,
  374. *    the buffers for the user paths are used.  The position of the point
  375. *    is calculated relative to the position of the previous point. If
  376. *    this is the first set of points in the description then put the
  377. *    absolute values in place, otherwise use the lastPoint 
  378. *    argument to calculate the displacement.
  379. */
  380. - putControlUPath:(UPath *)drawUpath  forRect:(NXRect *)r  :(NXPoint *) lastPoint  
  381. {
  382.     int        i, j;
  383.  
  384.     NXRect        bounds;
  385.  
  386.     [self getBounds:&bounds  withKnobs:YES];
  387.     if (!r || NXIntersectsRect(r, &bounds))
  388.     {
  389.         i = drawUpath->num_ops;
  390.         drawUpath->ops[i++] = 'a';
  391.         drawUpath->ops[i++] = 'b';
  392.         drawUpath->ops[i++] = 'b';
  393.         drawUpath->ops[i++] = 'a';
  394.         drawUpath->num_ops += PTS_BEZIER;
  395.  
  396.         i = drawUpath->num_pts;
  397.         if (i == 0)
  398.         {        
  399.             drawUpath->pts[i++] = path.pts[4];
  400.             drawUpath->pts[i++] = path.pts[5];
  401.         }
  402.         else
  403.         {
  404.             drawUpath->pts[i++] = path.pts[4] - lastPoint->x;
  405.             drawUpath->pts[i++] = path.pts[5] - lastPoint->y;
  406.         }
  407.  
  408.         for (j = 2; i <  PTS_BEZIER * 2;  j++, i++)
  409.             drawUpath->pts[i] = path.pts[j + 4] - path.pts[j + 2];
  410.  
  411.          drawUpath->num_pts += PTS_BEZIER*2;
  412.         lastPoint->x = path.pts[10];
  413.         lastPoint->y = path.pts[11];
  414.     }
  415.     
  416.     return self;
  417. }
  418.  
  419. /*
  420. *    Place the description of the control lines into the the user path.
  421. *    Update the bounding box of the user path if necessary.
  422. */
  423. - putControlLinesUPath:(UPath *) drawUpath  forRect:(NXRect *) r
  424. {
  425.     int    i;
  426.  
  427.     NXRect        bounds;
  428.  
  429.     [self getBounds:&bounds  withKnobs:YES];
  430.     if (!r || NXIntersectsRect(r, &bounds))
  431.     {
  432.         i = drawUpath->num_ops;
  433.         drawUpath->ops[ i++] = dps_moveto;
  434.         drawUpath->ops[ i++] = dps_lineto;
  435.         drawUpath->ops[ i++] = dps_moveto;
  436.         drawUpath->ops[ i++] = dps_lineto;
  437.         drawUpath->num_ops += 4;
  438.  
  439.         for (i=0; i < PTS_BEZIER * 2; i++)
  440.             drawUpath->pts[drawUpath->num_pts + i] = path.pts[i + 4];
  441.         drawUpath->num_pts += PTS_BEZIER*2;
  442.  
  443.         drawUpath->pts[0] = MIN(LLX(path.pts), drawUpath->pts[0]);
  444.         drawUpath->pts[1] = MIN(LLY(path.pts), drawUpath->pts[1]);
  445.         drawUpath->pts[2] = MAX(URX(path.pts), drawUpath->pts[2]);
  446.         drawUpath->pts[3] = MAX(URY(path.pts), drawUpath->pts[3]);
  447.     }
  448.             
  449.     return self;
  450. }
  451.  
  452. /*
  453.  *    Draws the graphic if it lies within the bounds of the rectangle passed in.
  454.  *    Draws with the ucache on if uFlag is YES.
  455.  */
  456. - drawObject:(NXRect *)r  withUcache:(BOOL)uFlag
  457. {
  458.     int            start_pt = 1;
  459.  
  460.     NXRect        bounds;
  461.  
  462.     [self getBounds:&bounds  withKnobs:NO];
  463.     if (!r || NXIntersectsRect(r, &bounds))
  464.     {
  465.         PSsetgray(COLOR); 
  466.         PSsetlinewidth(WIDTH);
  467.         if (uFlag)
  468.         {
  469.             path.ops[0] = dps_ucache;
  470.             start_pt = 0;
  471.         }
  472.         DPSDoUserPath(&path.pts[4], path.num_pts-4, dps_float,
  473.             &path.ops[start_pt], path.num_ops - start_pt, path.pts, dps_ustroke);
  474.     }
  475.  
  476.     return self;
  477. }
  478.  
  479. @end
  480.